module CPU(CLK, reset, out, dataout);
   input wire CLK;
   input wire reset;
   output wire [31:0] out;
   output wire [31:0] dataout;
   wire [31:0] newPC, PCplus, EXEMEM_PCjump,MEMWB_PCjump , pc2imem, IFID_instruction;
   wire        pcsel;
   wire nop;
   wire [31:0] memoryout, r31;

   wire [31:0] IDEXE_pc, EXEMEM_pc;
   wire [31:0] IDEXE_instruction, EXEMEM_instruction, MEMWB_instruction, WB_instruction;
   wire        IDEXE_reg_wen, EXEMEM_reg_wen, MEMWB_reg_wen, WB_reg_wen;
   wire        IDEXE_wb_sel, EXEMEM_wb_sel, MEMWB_wb_sel, WB_wb_sel;
   wire [4:0]  IDEXE_reg_waddr, EXEMEM_reg_waddr, MEMWB_reg_waddr, WB_reg_waddr;
   wire        IDEXE_mem_wen, EXEMEM_mem_wen, MEMWB_mem_wen, WB_mem_wen;
   wire        IDEXE_isjlt, EXEMEM_isjlt;
   wire        IDEXE_isjeq, EXEMEM_isjeq;
   wire [3:0]  IDEXE_aluop, EXEMEM_aluop;
   wire        IDEXE_alusrc2_sel, EXEMEM_alusrc2_sel;
   wire [15:0] IDEXE_aluimm, EXEMEM_aluimm;
   wire [31:0] IDEXE_regout1, EXEMEM_regout1, MEMWB_regout1, WB_regout1,
               IDEXE_regout2, EXEMEM_regout2, MEMWB_regout2, WB_regout2,
               IDEXE_regout3, EXEMEM_regout3, MEMWB_regout3, WB_regout3;
   wire [31:0] MEMWB_memoryout, WB_memoryout;
   wire [31:0] EXEMEM_alusrc2;
   wire [32:0] EXEMEM_aluresult, MEMWB_aluresult, WB_aluresult;

   wire [31:0] wbdata;

   //-------IF/ID----------------------------
   Mux pcmux(.src1(PCplus), .src2(EXEMEM_PCjump), .sel(pcsel), .out(newPC));
   assign pcsel = (EXEMEM_isjeq && EXEMEM_aluresult == 0) || (EXEMEM_isjlt && EXEMEM_aluresult[32] == 1);
   PC pc(.CLK(CLK), .newaddr(newPC), .addr(pc2imem), .reset(reset), .block(nop));
   Adder pcadder(.A(pc2imem), .B(1), .result(PCplus));
   Instruction_memory imem(.CLK(CLK), .i_address(pc2imem),
                           .instruction(IFID_instruction), .reset(reset));
   IFID myIFID(.CLK(CLK),.pc_in(pc2imem),
               .pc_out(IDEXE_pc),
               .instruction_in(IFID_instruction),
               .instruction_out(IDEXE_instruction),
               .block(nop), .reset(reset));

   //--------ID/EXE---------------------
   Decoder decoder(.opcode(IDEXE_instruction),
                   .reg_wen(IDEXE_reg_wen),
                   .wb_sel(IDEXE_wb_sel),
                   .reg_waddr(IDEXE_reg_waddr),
                   .mem_wen(IDEXE_mem_wen),
                   .isjeq(IDEXE_isjeq),
                   .isjlt(IDEXE_isjlt),
                   .aluop(IDEXE_aluop),
                   .alusrc2_sel(IDEXE_alusrc2_sel),
                   .aluimm(IDEXE_aluimm));
   Reg_file reg_file( .raddr1(IDEXE_instruction[23:19]),
                      .raddr2(IDEXE_instruction[18:14]),
                      .raddr3(IDEXE_instruction[13:9]),
                      .out1(IDEXE_regout1),
                      .out2(IDEXE_regout2),
                      .out3(IDEXE_regout3),
                      .wen(WB_reg_wen),
                      .in(wbdata),
                      .waddr(WB_reg_waddr),
                      .clk(CLK), .reset(reset), .r31(r31));
    // Inserting NOP
   NOPDETECTOR myNOPDETECTOR(.nop(nop), .instruction(IDEXE_instruction),
                             .raddr1(IDEXE_instruction[23:19]),
                             .raddr2(IDEXE_instruction[18:14]),
                             .raddr3(IDEXE_instruction[13:9]),
                             .EXEMEM_reg_waddr(EXEMEM_reg_waddr),
                             .EXEMEM_reg_wen(EXEMEM_reg_wen),
                             .MEMWB_reg_waddr(MEMWB_reg_waddr),
                             .MEMWB_reg_wen(MEMWB_reg_wen));
   IDEXE myIDEXE(.CLK(CLK), .reset(reset),
                 .instruction_in(IDEXE_instruction), .instruction_out(EXEMEM_instruction),
                 .pc_in(IDEXE_pc), .pc_out(EXEMEM_pc),
                 .reg1_in(IDEXE_regout1), .reg1_out(EXEMEM_regout1),
                 .reg2_in(IDEXE_regout2), .reg2_out(EXEMEM_regout2),
                 .reg3_in(IDEXE_regout3), .reg3_out(EXEMEM_regout3),

                 .reg_wen_in(IDEXE_reg_wen), .reg_wen_out(EXEMEM_reg_wen),
                 .wb_sel_in(IDEXE_wb_sel), .wb_sel_out(EXEMEM_wb_sel),
                 .reg_waddr_in(IDEXE_reg_waddr), .reg_waddr_out(EXEMEM_reg_waddr),

                 .mem_wen_in(IDEXE_mem_wen), .mem_wen_out(EXEMEM_mem_wen),
                 .isjeq_in(IDEXE_isjeq), .isjeq_out(EXEMEM_isjeq),
                 .isjlt_in(IDEXE_isjlt), .isjlt_out(EXEMEM_isjlt),

                 .aluop_in(IDEXE_aluop), .aluop_out(EXEMEM_aluop),
                 .alusrc2_sel_in(IDEXE_alusrc2_sel), .alusrc2_sel_out(EXEMEM_alusrc2_sel),
                 .aluimm_in(IDEXE_aluimm), .aluimm_out(EXEMEM_aluimm),
                 .nop(nop));


   //------EX/MEM-------------------------------------------
   Adder pcjumpadder(.A(EXEMEM_pc), .B({ {18{EXEMEM_instruction[13]}}, EXEMEM_instruction[13:0] }), .result(EXEMEM_PCjump));
   Mux alusrc2mux(.src1(EXEMEM_regout2), .src2({16'b0,EXEMEM_aluimm}), .sel(EXEMEM_alusrc2_sel), .out(EXEMEM_alusrc2));
   Alu alu(.op(EXEMEM_aluop), .operand1(EXEMEM_regout1), .operand2(EXEMEM_alusrc2), .result(EXEMEM_aluresult));
   EXEMEM myEXEMEM(.CLK(CLK), .reset(reset),
                   .pcjump_in(EXEMEM_PCjump),.pcjump_out(MEMWB_PCjump),
                   .reg3_in(EXEMEM_regout3), .reg3_out(MEMWB_regout3),

                   .reg_wen_in(EXEMEM_reg_wen), .reg_wen_out(MEMWB_reg_wen),
                   .wb_sel_in(EXEMEM_wb_sel), .wb_sel_out(MEMWB_wb_sel),
                   .reg_waddr_in(EXEMEM_reg_waddr), .reg_waddr_out(MEMWB_reg_waddr),

                   .mem_wen_in(EXEMEM_mem_wen), .mem_wen_out(MEMWB_mem_wen),
                   .isjeq_in(EXEMEM_isjeq), .isjeq_out(MEMWB_isjeq),
                   .isjlt_in(EXEMEM_isjlt), .isjlt_out(MEMWB_isjlt),

                   .aluresult_in(EXEMEM_aluresult), .aluresult_out(MEMWB_aluresult));

   //---------MEM/WB--------
   Memory datamemory(.raddr(MEMWB_aluresult[31:0]), .waddr(MEMWB_aluresult[31:0]),.clk(CLK),
                     .wen(MEMWB_mem_wen), .in(MEMWB_regout3), .out(MEMWB_memoryout), .reset(reset));
   WB myWB (.CLK(CLK), .reset(reset),
            .reg_wen_in(MEMWB_reg_wen), .reg_wen_out(WB_reg_wen),
            .wb_sel_in(MEMWB_wb_sel), .wb_sel_out(WB_wb_sel),
            .reg_waddr_in(MEMWB_reg_waddr), .reg_waddr_out(WB_reg_waddr),
            .aluresult_in(MEMWB_aluresult), .aluresult_out(WB_aluresult),
            .memoryout_in(MEMWB_memoryout), .memoryout_out(WB_memoryout));

   //------------WB-----------------
   Mux wbmux(.src1(WB_aluresult[31:0]),.src2(WB_memoryout), .sel(WB_wb_sel), .out(wbdata));
   assign out = pc2imem;
   assign dataout = r31;
endmodule
